/* 
 * Shuriken is the legal property of its developers. Please refer to the
 * COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Shuriken.Core; 

namespace Shuriken.Core.Utils
{
	/// <summary>
	/// Summary description for Hotkey.
	/// </summary>
	public class Hotkey
	{
		#region interop stuff
		[DllImport("user32.dll")]
		static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);

		[DllImport("user32.dll", SetLastError=true)]
		static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

        [DllImport("user32.dll", SetLastError = true)]
		static extern bool UnregisterHotKey(IntPtr hWnd, int id);

		[DllImport("user32.dll")]
		static extern uint MapVirtualKey(uint uCode, uint uMapType);

		[DllImport("user32.dll")]
		static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

		[DllImport("user32.dll")]
		static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

		[DllImport("user32.dll")]
		static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
        
		const byte VK_SHIFT = 0x10,
			VK_CONTROL = 0x11,
			VK_MENU = 0x12,
			VK_SPACE = 0x20,
			VK_C = 0x43,
			VK_F1 = 0x70,
			VK_F2 = 0x71,
			VK_F3 = 0x72,
			VK_F4 = 0x73,
			VK_F5 = 0x74,
			VK_F6 = 0x75,
			VK_F7 = 0x76,
			VK_F8 = 0x77,
			VK_F9 = 0x78,
			VK_F10 = 0x79,   
			VK_F11 = 0x7A,
			VK_F12 = 0x7B;

		const uint KEYEVENTF_KEYDOWN = 0x0000,
			KEYEVENTF_EXTENDEDKEY = 0x0001,
			KEYEVENTF_KEYUP = 0x0002,
			KEYEVENTF_UNICODE = 0x0004,
			KEYEVENTF_SCANCODE = 0x0008;

		public const uint MOD_ALT = 0x0001,
			MOD_CONTROL = 0x0002,
			MOD_SHIFT = 0x0004,
			MOD_WIN = 0x0008;

		const int WM_HOTKEY = 0x312,
			WM_DRAWCLIPBOARD = 0x308,
			WM_CHANGECBCHAIN = 0x30D;
		#endregion 

        ///// <summary>
        ///// Registers the hotkey for frmHidden
        ///// </summary>
        //public static bool RegisterFormHotkey()
        //{
        //    //
        //    // Register CTRL+SPACE as a hot key combination
        //    return RegisterHotKey(frmHidden.GetInstance().Handle , 1, MOD_CONTROL, (uint)VK_SPACE);			
        //}

		/// <summary>
		/// Unregisters hotkey for frmHidden
		/// </summary>
        /// <returns>true, if succeeded</returns>
		public static bool UnregisterFormHotkey()
		{
			//
			// Unregister hot key
			return UnregisterHotKey(frmHidden.GetInstance().Handle, 1);
		}

        /// <summary>
        /// Registers a hotkey for the application 
        /// </summary>
        /// <param name="hotKey">hotkey to register</param>
        /// <returns>false if the registration was not successfull</returns>
        /// <remarks>Registers a hotkey from the application, use Marshall.GetLastWin32Error to get the error code.</remarks>
        public static bool RegisterFormHotkey(System.Windows.Forms.Keys hotKey)
        {
            uint modifiers = 0;
            uint key = 0;
            bool result; 

            KeyEventArgs keyarg = new KeyEventArgs(hotKey);

            // Shift modifier key 
            if (keyarg.Shift)
            {
                modifiers |= MOD_SHIFT;                 
            }

            // Alt modifier key 
            if (keyarg.Alt)
            {
                modifiers |= MOD_ALT; 
            }

            // Control modifier key 
            if (keyarg.Control)
            {
                modifiers |= MOD_CONTROL; 
            }

            UnregisterFormHotkey(); 

            key = (uint)keyarg.KeyCode;
            result = RegisterHotKey(frmHidden.GetInstance().Handle, 1, modifiers, key);

            if (!result)
            {
                int error_code = Marshal.GetLastWin32Error();
                throw new Exception("Failed to register hotkey " + hotKey.ToString()+ " with error: " + error_code + "!");
            }


            return result; 
        }

        /// <summary>
        /// Registers hotkey at application startup. 
        /// </summary>
        /// <remarks>
        /// Registers hotkey at application startup. Reads the hotkey from settings or uses
        /// default hotkey if not possible to read from settings. 
        /// This method will display a messagebox if the registration fails. 
        /// </remarks>
        public static void RegisterHotkeyAtStartup()
        {
            Keys hotKey;

            try
            {
                hotKey = ApplicationEnvironment.Preferences.HotKey; 
                Hotkey.RegisterFormHotkey(hotKey);
            }
            catch (Exception excp)
            {
                // exception is thrown if the hotkey is not set in settings
                // setting it to default. 
                MessageBox.Show(excp.Message, "Failed to set hotkey");
            }

        }

        /// <summary>
        /// Releasing hotkey temprarly
        /// </summary>
        /// <remarks>
        /// This function is used for releasing the hotkey temproraly. It is just a simple unregister with a fancy name. 
        /// One typicall use is to use when the settings dialog is loaded, so even the current hotkey can be selected. 
        /// </remarks>
        public static bool ReleaseHotkeyTemporarly()
        {
            return UnregisterFormHotkey();
        }

        /// <summary>
        /// Acquire hotkey. 
        /// </summary>
        /// <remarks>
        /// Acquire hotkey is used in pairs with ReleaseHotkeyTemporarly. It is used to get the current hotkey to be reregistered.
        /// It is going to show a message box in case it fails and returns false as result. 
        /// </remarks>
        /// <returns>true, if the operation is successful</returns>
        public static bool AcquireHotkeyAgain()
        {
            try
            {
                return Hotkey.RegisterFormHotkey(ApplicationEnvironment.Preferences.HotKey);
            }
            catch (Exception excp)
            {
                // Failed to register hotkey, display error message
                MessageBox.Show("There were problems registering the changes you made. " + excp.Message + "!");
            }

            return false; 
        }
    }


}
